import * as Cesium from '@cesiumjs';
import {
  G3xObjBase
} from "@g3xObjBase"
import * as g3xUtils from "@g3x/utils";
import {
  autorun,
  reaction,
  intercept,
  extendObservable,
  computed
} from 'mobx';
import {
  computeHpr
} from '../Camera';
import {
  interpolatePosition
} from './interpolate';
import {G3xPolyline} from '@g3vObjects';

import initSchema from "./schema";
import extendsClass from "./extends";

var flyCartesian = void 0;
var _updateCurrentPositionAndRotation_anotherPos = void 0;

function computeRotationAlongThePath(d, currentPos, ds, length, positions, currentRotation) {
  _updateCurrentPositionAndRotation_anotherPos = _updateCurrentPositionAndRotation_anotherPos || [0, 0, 0];
  var anotherPos = _updateCurrentPositionAndRotation_anotherPos;
  var firstPos = void 0;
  var nextPos = void 0;
  var nextD = d + 0.01;
  if (nextD > length) {
    nextD = d - 0.01;
    if (nextD >= 0) {
      interpolatePosition(nextD, ds, positions, anotherPos);
      firstPos = anotherPos;
      nextPos = currentPos;
    }
  } else {
    interpolatePosition(nextD, ds, positions, anotherPos);
    firstPos = currentPos;
    nextPos = anotherPos;
  }

  if (firstPos && nextPos) {
    computeHpr(firstPos, nextPos, currentRotation);
    currentRotation[0] += Math.PI * 0.5;
  }

  return currentRotation;
}

/**
 * 路径
 * @memberof Obj
 * @class
 * @extends XbsjCzmObj
 */
class G3xPath extends G3xObjBase {
  constructor(viewer, options) {

    super(options);
    g3xUtils.g3x_defineReadonly(this, "viewer", viewer)
    this.init()
  }

  init() {
    var _this = this;
    const {viewer,disposers} = this;

    const polyline = new G3xPolyline(viewer);
    _this._polyline = polyline;

    disposers.push(function() {
      return polyline.destroy();
    });
    disposers.push(g3xUtils.track(polyline, 'positions', _this, 'positions'));
    disposers.push(g3xUtils.track(polyline, 'loop', _this, 'loop'));

    polyline.show = _this.enabled && _this.show && _this.showHelper;
    disposers.push(reaction(function() {
      return _this.enabled && _this.show && _this.showHelper;
    }, function(show) {
      polyline.show = show;
      polyline.showHelper = show;
    }));

    /**
     * 是否处于创建状态<br/>
     * 设置为true以后，将进入重新创建的状态；此时可以使用鼠标左键在三维窗口中点击来创建路径的关键点，当点击鼠标右键，则表示创建完成。此时该属性会自动变成false。
     * @type {boolean}
     * @instance
     * @default false
     * @name creating
     * @memberof Obj.Path
     */
    // disposers.push((0, _registerInteractionProperty.registerPolylineCreating)(_this._earth, _this, {
    //   polylineCreatingProperty: 'creating'
    // }));

    /**
     * 是否处于编辑状态<br/>
     * 设置为true以后，将进入重新创建的状态；此时可以使用鼠标左键在三维窗口中选取需要修改路径的关键点，当点击鼠标右键，则表示编辑完成。此时该属性会自动变成false。
     * @type {boolean}
     * @instance
     * @default false
     * @name editing
     * @memberof Obj.Path
     */
    // _this.disposers.push((0, _registerInteractionProperty.registerPolylineEditing)(_this._earth, _this, {
    //   polylineEditingProperty: 'editing'
    // }));

    _this._cameras = [];
    disposers.push(function() {
      _this._cameras.forEach((e)=>e.destroy());
      _this._cameras.splice(0)
    });

    // 这是为了防止positions被换成新的数组，从而导致下面的observe失效！
    disposers.push(intercept(_this, 'rotations', function(changed) {
      if (changed.type === 'update') {
        var _this$rotations;
        (_this$rotations = _this.rotations).splice.apply(_this$rotations, [0, _this.rotations.length, ...changed.newValue]);
      }
      return null;
    }));

    disposers.push(intercept(_this.rotations, function(changed) {
      if (changed.type === 'splice') {
        var _this$_cameras;
        for (var i = changed.index; i < changed.index + changed.removedCount; ++i) {
          _this._cameras[i].destroy();
        }
        var cameras = changed.added.map(function() {
          return new _CameraHelper2.default(_this._earth); // todo
        });
        (_this$_cameras = _this._cameras).splice.apply(_this$_cameras, [changed.index, changed
          .removedCount, ...cameras
        ]);
      }

      return changed;
    }));

    disposers.push(reaction(function() {
      return {
        positions: _this.positions.map((e)=>[...e]),
        rotations: _this.rotations.map((e)=>[...e]),
        show: _this.enabled && _this.show && _this.showDirection
      };
    }, function() {
      // 中间状态positions增加了，rotations还没增加上
      if (_this.positions.length !== _this.rotations.length) {
        return;
      }
      for (var i = 0; i < _this.positions.length; ++i) {
        var rotation = _this.rotations[i];
        var position = _this.positions[i];
        _this._cameras[i].position = position;
        _this._cameras[i].rotation = rotation;
      }

      var show = _this.enabled && _this.show && _this.showDirection;
      _this._cameras.forEach(function(camera) {
        return camera.show = show;
      });
    }));

    _this._curve = new G3xPolyline(viewer);
    _this._curve.arcType = 'NONE';
    _this._curve.show = _this.enabled && _this.show;
    disposers.push(function() {
      return _this._curve = _this._curve && _this._curve.destroy();
    });
    disposers.push(reaction(function() {
      return _this.enabled && _this.show;
    }, function(show) {
      _this._curve.show = show;
    }));

    extendObservable(_this, {
      /**
       * 当前步进距离<br/>
       * 指当前播放状态下由第一个关键点沿路径运动到当前位置的长度，单位是米。<br/>
       * 当前playing属性为false时，此属性恒定不变；当playing为true，此属性会自动增加。
       * 随着currentD的变化，会实时计算currentPosition、currentRotation这两个属性。
       * currentPosition指当前位置，currentRotation指当前姿态
       * @type {number}
       * @instance
       * @default 0
       * @memberof Obj.Path
       */
      currentD: 0,
      /**
       * 当前相机是否可见<br/>
       * 当前相机指位于currentPosition处的一个相机。<br/>
       * 此属性，主要用来显示当前播放的位置，方便调试。<br/>
       * 注意：当设置cameraAttached为true时，务必手动将此属性设置为false。否则相机绑定以后，会发现前方有一个物体遮挡整个画面！
       * @type {boolean}
       * @instance
       * @default false
       * @memberof Obj.Path
       */
      currentShow: false,
      /**
       * 当前播放速度，单位为米/秒。<br/>
       * 注意，此属性可以实时修改，修改后的速度立即生效，所以可以动态修改路径上物体的前进速度！
       * @type {number}
       * @instance
       * @default 50.0,
       * @memberof Obj.Path
       */
      // currentSpeed: 50.0,
      /**
       * 是否循环播放<br/>
       * 如果为false，则playing设置为true时，会从当前位置播放到最后一个关键点，并停止播放，此时playing属性会自动变成false。
       * 若此属性为true时，播放到最后一个关键点以后，将自动重第一个关键点继续播放。
       * @type {boolean}
       * @instance
       * @default false,
       * @memberof Obj.Path
       */
      // loopPlay: false,
      _currentPosition: [0, 0, 0],
      _currentRotation: [0, 0, 0],
      /**
       * 当前相机位置，形式如：[0, 0, 0]<br/>
       * 该数组中的元素分别表示经度（单位弧度）、纬度（单位弧度）、高度（单位米）。
       * 注意该属性，为只读属性！
       * @type {number[]}
       * @instance
       * @readonly
       * @memberof Obj.Path
       */
      get currentPosition() {
        return this._currentPosition;
      },
      /**
       * 当前相机姿态，形式如：[0, 0, 0]<br/>
       * 该数组中的元素分别表示偏航角（单位弧度）、俯仰角（单位弧度）、翻转角（单位弧度）。
       * 注意该属性，为只读属性！
       * @type {number[]}
       * @instance
       * @readonly
       * @memberof Obj.Path
       */
      get currentRotation() {
        return this._currentRotation;
      }
    }, {
      currentPosition: computed,
      currentRotation: computed
    });

    // this.fromJsonFilterKeys.push('currentD', 'currentShow', 'currentSpeed');
    _this.fromJsonFilterKeys.push('currentD', 'currentShow');

    disposers.push(reaction(function() {
      return {
        positions: _this.positions.map(e=>[...e]),
        slices: _this.slices,
        loop: _this.loop
      };
    }, function() {
      const {positions,slices,loop,_curve} = _this;
      if (positions.length < 3) {
        return;
      }
      interpolatePositions(positions, slices, loop, _curve.positions);
    }));

    extendObservable(_this, {
      _loopPositions: [],
      _loopRotations: [],
      _ds: [],
      get ds() {
        return this._ds;
      },
      /**
       * 当前路径的总长<br/>
       * 注意当loop设置true和false时，即使关键点没有变化，但是总长是会变化的。<br/>
       * 因为loop为true时，首末关键点会相连，这一段距离也会考虑到路径总长中。
       * @type {number[]}
       * @instance
       * @readonly
       * @memberof Obj.Path
       */
      get length() {
        if (this.loop && this._ds.length > 2) {
          return this._ds[this._ds.length - 2];
        } else {
          var d = this._ds.length === 0 ? 0 : this._ds[this._ds.length - 1];
          return d;
        }
      }
    }, {
      ds: computed,
      length: computed
    });

    disposers.push(reaction(function() {
      return {
        cameraAttached: _this.cameraAttached,
        loop: _this.loop,
        positions: _this.positions.map(e=>[...e]),
        rotations: _this.rotations.map(e=>[...e])
      };
    }, function() {
      _this._updateCurrentPositionAndRotation();
    }, {
      delay: 100
    }));

    disposers.push(observe(_this, 'currentD', function() {
      _this._updateCurrentPositionAndRotation();
    }));

    // this._currentCamera = createCamera(this._earth);
    _this._currentCamera = new _CameraHelper2.default(_this._earth);
    _this._currentPoints = new _Points2.default(_this._earth);
    disposers.push(function() {
      _this._currentCamera = _this._currentCamera && _this._currentCamera.destroy();
      _this._currentPoints = _this._currentPoints && _this._currentPoints.destroy();
    });
    _this._currentPoints.points.push({});

    var updateCurrentCameraPositionAndRotation = function updateCurrentCameraPositionAndRotation() {
      var currentShow = _this.enabled && _this.currentShow;
      _this._currentCamera.show = currentShow;
      _this._currentPoints.show = currentShow;

      if (!currentShow) {
        return;
      }

      _this._currentPoints.points[0].position = [].concat((0, _toConsumableArray3.default)(_this
        .currentPosition));
      _this._currentCamera.position = _this.currentPosition;
      _this._currentCamera.rotation = _this.currentRotation;
    };

    disposers.push(reaction(function() {
      return {
        show: _this.enabled && _this.currentShow,
        position: [].concat((0, _toConsumableArray3.default)(_this.currentPosition)),
        rotation: [].concat((0, _toConsumableArray3.default)(_this.currentRotation))
      };
    }, updateCurrentCameraPositionAndRotation));

    extendObservable(_this, {
      _target: [0, 0, 0],
      _targetEnabled: false
    });

    disposers.push(reaction(function() {
      return {
        _targetEnabled: _this._targetEnabled,
        _target: _this._target && [..._this._target]||undefined
      };
    }, function() {
      _this._targetEnabled && _this.targetTo(_this._target);
    }));

    /**
     * 是否处于指定所有关键点相机的目标的状态<br/>
     * 设置为true以后，所有关键点相机都会同时旋转，指向目标点。鼠标左键点击以后，该属性会自动变为false。
     * 注意：关键点相机状态需要开启showDirection属性以后，才能方便观察！
     * @type {boolean}
     * @instance
     * @default false
     * @name targetPicking
     * @memberof Obj.Path
     */
    disposers.push((0, _registerInteractionProperty.registerPositionPicking)(_this._earth, _this, {
      positionPickingProperty: 'targetPicking',
      positionProperty: '_target'
    }));

    disposers.push(intercept(_this, 'targetPicking', function(changed) {
      _this._targetEnabled = changed.newValue;
      return changed;
    }));

    /**
     * 是否处于播放状态<br/>
     * 如果属性值为true，则所有'current'开头的相关属性会动态发生变化。
     * 可以通过手动设置为false，来结束播放状态。
     * 当loopPlay属性为false时，playing属性会在路径播放到最后一个关键点的位置时，自动变为false。
     * @type {boolean}
     * @instance
     * @default false
     * @name playing
     * @memberof Obj.Path
     */
    extendObservable(_this, {
      _pathPlayingListener: undefined,
      get playing() {
        return !!this._pathPlayingListener;
      },
      set playing(val) {
        if (!!val && !this._pathPlayingListener) {
          this._play();
        } else if (!val && this._pathPlayingListener) {
          this._pathPlayingListener = this._pathPlayingListener && this._pathPlayingListener();
        }
      }
    }, {
      _pathPlayingListener: _mobx.observable.ref,
      playing: _mobx.computed
    });

    disposers.push(function() {
      _this.playing = false;
    });

    //     /**
    //      * 是否处于相机绑定状态<br/>
    //      * 当该属性为true时，当前相机会实时使用currentPosition属性指定位置，实时使用currentRotation属性来指定姿态。
    //      * 注意：当鼠标左键在三维窗口中点击时，会自动打断相机绑定状态，该属性会自动变为false。
    //      * @type {boolean}
    //      * @instance
    //      * @default false
    //      * @name cameraAttached
    //      * @memberof Obj.Path
    //      */
    //     extendObservable(this, {
    //         _currentFlight: undefined,
    //         get cameraAttached() {
    //             return !!this._currentFlight;
    //         },
    //         set cameraAttached(val) {
    //             const scene = this._earth.czm.scene;

    //             if (!!val && !this._currentFlight) {
    //                 this._currentFlight = {
    //                     cancelTween: () => {
    //                         this._currentFlight = undefined;
    //                     }
    //                 };
    //                 scene.camera.cancelFlight();
    //                 scene.camera._currentFlight = this._currentFlight;
    //             } else if (!val && this._currentFlight) {
    //                 if (this._currentFlight !== scene.camera._currentFlight) {
    //                     throw new Error('this._currentCamera !== scene.camera._currentFlight');
    //                 }
    //                 scene.camera.cancelFlight();
    //                 this._currentFlight = undefined;
    //             }
    //         }
    //     }, {
    //         _currentFlight: observable.ref,
    //         cameraAttached: computed,
    //     });

    //     this.disposers.push(() => {
    //         this.cameraAttached = false;
    //     });

    // cameraAttached
    disposers.push(_registerInteractionProperty.registerCameraTracking(_this.viewer, _this, {
      cameraAttachedFunc(tracking) {
        var _this2 = this;

        var disposers = [];
        disposers.push(reaction(function() {
          return {
            currentPosition: [..._this2.currentPosition],
            currentRotation: [..._this2.currentRotation],
            cameraAttachedOffsetPosition: [..._this2.cameraAttachedOffsetPosition],
            cameraAttachedOffsetRotation: [..._this2.cameraAttachedOffsetRotation],
            viewDistance: _this2.viewDistance
          };
        }, function(prv) {
          for (var i = 0; i < 3; ++i) {
            tracking.position[i] = prv.currentPosition[i] + prv.cameraAttachedOffsetPosition[i];
            tracking.rotation[i] = prv.currentRotation[i] + prv.cameraAttachedOffsetRotation[i];
          }
          tracking.viewDistance = prv.viewDistance;
        }));
        this.viewer.camera.tracking.isCameraLike = true;
        return function() {
          disposers.forEach(function(d) {
            return d();
          });
          disposers.length = 0;
        };
      }
    }));
  }
}

(0, extendsClass)(G3xPath);

(0, initSchema)(G3xPath);

// _XbsjObject2.default.registerType(Path, 'Path');

export default G3xPath;
export {G3xPath};
